package com.icesoft.core.web.helper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.icesoft.core.Const;
import com.icesoft.core.common.exception.CheckException;
import com.icesoft.core.common.exception.CodeErrorException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

public abstract class RequestHold {

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attrs == null) {
            throw new CodeErrorException("无法找到请求体，请不要在多线程中使用");
        }
        return attrs.getRequest();
    }

    public static String getRequestUrl(HttpServletRequest request) {
        return replaceContextPath(request.getRequestURI(), request.getContextPath());
    }

    public static String getContextPathPre(HttpServletRequest request) {
        String contextPath = request.getContextPath();
        if (isEmptyPath(contextPath)) {
            return contextPath;
        }
        return contextPath + "/";
    }

    public static String replaceContextPath(String url, String contextPath) {
        if (isEmptyPath(contextPath)) {
            return url;
        }
        return url.replaceFirst(contextPath, "");

    }

    private static boolean isEmptyPath(String contextPath) {
        return "/".equals(contextPath);
    }

    public static HttpServletRequest getRequestOrNull() {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attrs == null) {
            return null;
        }
        return attrs.getRequest();
    }

    public static String getString(HttpServletRequest request, String name) {
        String value = request.getParameter(name);
        if (value == null) {
            @SuppressWarnings("unchecked")
            Map<String, String> paramMap = (Map<String, String>) request
                    .getAttribute(Const.REQUEST_RESOLVER_PARAM_MAP_NAME);
            if (paramMap != null) {
                value = paramMap.get(name);
            }
        }
        return value;
    }

    public static Boolean getBoolean(HttpServletRequest request, String name) {
        String param = getString(request, name);
        return param == null ? null : Boolean.parseBoolean(param);
    }

    public static Integer getInteger(HttpServletRequest request, String name) {
        String param = getString(request, name);
        return param == null ? null : Integer.parseInt(param);
    }

    public static Long getLong(HttpServletRequest request, String name) {
        String param = getString(request, name);
        return param == null ? null : Long.parseLong(param);
    }

    /**
     * split ,
     */
    public static String[] getStringArray(HttpServletRequest request, String name) {
        return getStringArray(request, name, ",");
    }

    public static String[] getStringArray(HttpServletRequest request, String name, String split) {
        String[] values = request.getParameterValues(name);
        if (values == null || (values.length == 1 && values[0].contains(split))) {
            String param = getString(request, name);
            return param == null ? null : StringUtils.split(param, split);
        }
        if (values.length == 1 && StringUtils.isEmpty(values[0])) {
            return new String[0];
        }
        return values;
    }

    public static int[] getIntArray(HttpServletRequest request, String name, String split) {
        String[] arrStrs = getStringArray(request, name, split);
        if (arrStrs == null) {
            return new int[0];
        }
        int[] arrs = new int[arrStrs.length];
        for (int i = 0; i < arrs.length; i++) {
            arrs[i] = Integer.parseInt(arrStrs[i]);

        }
        return arrs;
    }

    public static Map<String, String> getRequestParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        Enumeration<?> pars = request.getParameterNames();
        while (pars.hasMoreElements()) {
            String par = (String) pars.nextElement();
            String[] values = request.getParameterValues(par);
            if (values.length == 1) {
                paramMap.put(par, values[0]);
            } else {
                StringBuilder vals = new StringBuilder();
                for (String v : values) {
                    vals.append(v).append(",");
                }
                if (vals.length() > 0) {
                    vals = new StringBuilder(vals.substring(0, vals.length() - 1));
                }
                paramMap.put(par, vals.toString());
            }

        }
        return paramMap;
    }

    /**
     * 获取登录用户的IP，可伪造的IP获取方式，但是可解决通过代理访问的问题
     */
    public static String getRemoteIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            return request.getRemoteAddr();
        }
        // 多个路由时，取第一个非unknown的ip
        final String[] arr = ip.split(",");
        for (final String str : arr) {
            if (!"unknown".equalsIgnoreCase(str)) {
                ip = str;
                break;
            }
        }
        return ip;
    }

    @Deprecated
    public static String getRemortIP(HttpServletRequest request) {
        return getRemoteIP(request);
    }

    public static boolean isLocalhost(HttpServletRequest request) {
        String remoteIp = getRemoteIP(request);
        return isLocalhost(remoteIp);
    }

    public static boolean isLocalhost(String ipAddr) {
        return "0:0:0:0:0:0:0:1".equals(ipAddr) || "127.0.0.1".equals(ipAddr);
    }

    private static String XMLHttpRequest = "XMLHttpRequest";

    public static boolean isAjax(HttpServletRequest request) {
        String xmlHttpRequest = request.getHeader("x-requested-with");
        if (xmlHttpRequest == null && request.getRequestURI().endsWith(".html")) {
            return false;
        }
        return XMLHttpRequest.equals(xmlHttpRequest);
    }

    public static String readBody(HttpServletRequest request) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i;
        InputStream is;
        try {
            is = request.getInputStream();
            while ((i = is.read()) != -1) {
                baos.write(i);
            }
            String paramStr = baos.toString();
            baos.close();
            return paramStr;
        } catch (IOException e) {
            throw new CheckException("流读取出错");
        }
    }

    public static String getRequestParamString(HttpServletRequest request) {
        Object param = request.getAttribute(Const.REQUEST_RESOLVER_PARAM_MAP_NAME);
        if (param == null) {
            String contextType = request.getContentType();
            try {
                if (contextType != null && contextType.contains(MediaType.APPLICATION_JSON_VALUE)
                        && request.getInputStream().isReady()) {
                    param = RequestHold.readBody(request);
                } else {
                    param = new ObjectMapper().writeValueAsString(request.getParameterMap());
                }
            } catch (Exception e) {
                throw new CheckException("request流读取出错");
            }
        }
        return param.toString();
    }
}
